﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using System;

namespace CHFramework
{
    public class UIManager : MonoBehaviour
    {
        private static UIManager _Instance = null;

        //窗体预设路径
        private Dictionary<EnumPanel, string> UIPathDic = null;
        //所有UI窗体字典
        private Dictionary<EnumPanel, BaseUI> AllPanelDic = null;
        //当前显示的UI窗体
        private Dictionary<EnumPanel, BaseUI> CurrentShowPanelDic = null;
        //定义“栈”集合，存储显示当前所有[反向切换]的窗体
        private Stack<BaseUI> CurrentUIPanelStack = null;

        private Transform UIRoot = null;
        private Transform NormalRoot = null;
        private Transform ExceptNormalRoot = null;
        private Transform FixedRoot = null;
        private Transform PopupRoot = null;
        private Transform LoadingRoot = null;
        private Transform ScriptRoot = null;

        public static UIManager Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new GameObject("UIManager").AddComponent<UIManager>();
                return _Instance;
            }
        }

        /// <summary>
        /// 初始化
        /// </summary>
        private void Awake()
        {
            AllPanelDic = new Dictionary<EnumPanel, BaseUI>();
            CurrentShowPanelDic = new Dictionary<EnumPanel, BaseUI>();
            CurrentUIPanelStack = new Stack<BaseUI>();

            InitUIRootLoad();

            UIRoot = GameObject.FindGameObjectWithTag(SysDefine.TagCanvas).transform;
            NormalRoot = GameUlities.FindTheChildNode(UIRoot.gameObject, SysDefine.NormalUIRoot);
            ExceptNormalRoot = GameUlities.FindTheChildNode(UIRoot.gameObject, SysDefine.ExceptNormalUIRoot);
            FixedRoot = GameUlities.FindTheChildNode(UIRoot.gameObject, SysDefine.FixedUIRoot);
            PopupRoot = GameUlities.FindTheChildNode(UIRoot.gameObject, SysDefine.PopupUIRoot);
            LoadingRoot = GameUlities.FindTheChildNode(UIRoot.gameObject, SysDefine.LoadingUIRoot);
            ScriptRoot = GameUlities.FindTheChildNode(UIRoot.gameObject, SysDefine.ScriptUIRoot);

            this.transform.SetParent(ScriptRoot, false);
            DontDestroyOnLoad(UIRoot);

            if (UIPathDic == null)
            {
                UIPathDic = new Dictionary<EnumPanel, string>() {
                    { EnumPanel.UILoginPanel, SysDefine.UIPanelPath + "UILoginPanel"},
                    { EnumPanel.UICreatePanel, SysDefine.UIPanelPath + "UICreatePanel"},
                    { EnumPanel.UIMainPanel, SysDefine.UIPanelPath + "UIMainPanel"},
                    { EnumPanel.UITopMainPanel, SysDefine.UIPanelPath + "UITopMainPanel"},
                    { EnumPanel.UIShopPanel, SysDefine.UIPanelPath + "UIShopPanel"},
                    { EnumPanel.UILoadingPanel, SysDefine.UIPanelPath + "UILoadingPanel"},
                    { EnumPanel.UIInGamePanel, SysDefine.UIPanelPath + "UIInGamePanel"},
                    { EnumPanel.UISettingPanel, SysDefine.UIPanelPath + "UISettingPanel"},
                    { EnumPanel.UIPausePanel, SysDefine.UIPanelPath + "UIPausePanel"}
                };                
            }
        }

        #region 对外接口
        /// <summary>
        /// 显示（打开）UI窗体
        /// </summary>
        /// <param name="panel"></param>
        public BaseUI ShowUIPanel(EnumPanel panel, params object[] args)
        {
            //UI窗体基类
            BaseUI baseUI = null;

            if (panel == EnumPanel.None) return null;

            baseUI = LoadUIPanelToAllUIPanelCache(panel);
            if (baseUI == null) return null;

            //是否清空栈集合中的数据
            if (baseUI.CurrentUIType.IsClearStack)
                ClearStackArray();

            //根据不同UI窗体的显示模式，分别做不同的加载处理
            switch (baseUI.CurrentUIType.ShowMode)
            {
                case UIPanelShowMode.Normal:
                    LoadUIToCurrentCache(panel);
                    break;
                case UIPanelShowMode.ReverseChange:
                    PushUIPanelToStack(panel);
                    break;
                case UIPanelShowMode.HideOther:
                    EnterUIPanelAndHideOthers(panel);
                    break;
            }
            return baseUI;
        }

        /// <summary>
        /// 关闭指定窗体
        /// </summary>
        /// <param name="panel"></param>
        public void CloseUIPanel(EnumPanel panel)
        {
            BaseUI baseUI = null;               //窗体基类

            if (panel == EnumPanel.None) return;
            //所有UI窗体集合中，如果没有记录，则直接返回
            AllPanelDic.TryGetValue(panel, out baseUI);
            if (baseUI == null) return;

            //根据窗体不同显示类型，分别作不同的关闭处理
            switch (baseUI.CurrentUIType.ShowMode)
            {
                case UIPanelShowMode.Normal:
                    ExitUIPanel(panel);
                    break;
                case UIPanelShowMode.ReverseChange:
                    PopUIPanel();
                    break;
                case UIPanelShowMode.HideOther:
                    ExitUIPanelAndShowOthers(panel);
                    break;
            }
        }

        /// <summary>
        /// 关闭当前窗体，且返回到上一个窗体
        /// </summary>
        public void CloseUIPanelAndReturnToLast()
        {
            PopUIPanel();
        }
        #endregion

        #region 私有方法
        //初始换加载（根UI节点）UIRoot预设
        private void InitUIRootLoad()
        {
            ResourcesMgr.Instance.LoadAndInstantiateObject(SysDefine.UIRootPath, false);
        }

        /// <summary>
        /// 根据UI窗体类型，加载到所有窗体缓存集合中
        /// </summary>
        /// <param name="panel"></param>
        /// <returns></returns>
        private BaseUI LoadUIPanelToAllUIPanelCache(EnumPanel panel)
        {
            //加载的返回UI窗体基类
            BaseUI baseUI = null;

            AllPanelDic.TryGetValue(panel, out baseUI);
            if(baseUI == null)
            { 
                //加载指定名字的窗体
                baseUI = LoadUIPanel(panel);
            }
            return baseUI;
        }

        private BaseUI LoadUIPanel(EnumPanel panel)
        {
            string uiPath = string.Empty;               //UI窗体路径
            GameObject cloneUIPrefab = null;            //创建的UI克隆体预设
            BaseUI baseUI = null;                       //窗体基类

            UIPathDic.TryGetValue(panel, out uiPath);

            if (!string.IsNullOrEmpty(uiPath))
                cloneUIPrefab = ResourcesMgr.Instance.LoadAndInstantiateObject(uiPath, false);

            //设置UI克隆体的父节点（根据克隆体中带的脚本中不同的位置信息）
            if (UIRoot != null && cloneUIPrefab != null)
            {
                baseUI = cloneUIPrefab.GetComponent<BaseUI>();
                if (baseUI == null)
                {
                    Debug.LogError("baseUI == null! , 请先确认窗体预设对象上是否加载了baseUI的子类");
                    return null;
                }

                switch (baseUI.CurrentUIType.PanelType)
                {
                    case UIPanelType.Normal:
                        GameUlities.AddChildToParent(NormalRoot, cloneUIPrefab.transform);
                        break;
                    case UIPanelType.ExceptNormal:
                        GameUlities.AddChildToParent(ExceptNormalRoot, cloneUIPrefab.transform);
                        break;
                    case UIPanelType.Fixed:
                        GameUlities.AddChildToParent(FixedRoot, cloneUIPrefab.transform);
                        break;
                    case UIPanelType.Popup:
                        GameUlities.AddChildToParent(PopupRoot, cloneUIPrefab.transform);
                        break;
                    case UIPanelType.Loading:
                        GameUlities.AddChildToParent(LoadingRoot, cloneUIPrefab.transform);
                        break;
                }

                //设置隐藏
                cloneUIPrefab.SetActive(false);

                //加入到所有UI窗体缓存集合中
                AllPanelDic.Add(panel, baseUI);
                return baseUI;
            }
            else
                Debug.LogError("UIRoot == null or cloneUIPrefab == null !， Please check !");

            return null;
        }

        /// <summary>
        /// 是否清空栈集合中数据
        /// </summary>
        /// <returns></returns>
        private bool ClearStackArray()
        {
            if (CurrentUIPanelStack != null && CurrentUIPanelStack.Count > 0)
            { 
                //清空栈集合
                CurrentUIPanelStack.Clear();
                return true;
            }
            return false;
        }

        /// <summary>
        /// 把当前窗体加载到当前窗体集合中
        /// </summary>
        /// <param name="panel"></param>
        private void LoadUIToCurrentCache(EnumPanel panel)
        {
            BaseUI baseUI = null;                   //UI窗体基类
            BaseUI baseUIFromAllCache = null;       //从所有窗体集合中得到窗体

            //如果正在显示的集合中，存在整个UI窗体，则直接返回
            CurrentShowPanelDic.TryGetValue(panel, out baseUI);
            if (baseUI != null) return;

            //把当前窗体，加载到正在显示的集合中
            AllPanelDic.TryGetValue(panel, out baseUIFromAllCache);
            if(baseUIFromAllCache != null)
            {
                CurrentShowPanelDic.Add(panel, baseUIFromAllCache);
                baseUIFromAllCache.Show();
            }
        }

        /// <summary>
        /// UI窗体入栈
        /// </summary>
        /// <param name="panel"></param>
        private void PushUIPanelToStack(EnumPanel panel)
        {
            BaseUI baseUI = null;               //UI窗体基类

            //判断栈集合中，是否有其他的窗体，有则冻结处理
            if (CurrentUIPanelStack.Count > 0)
            {
                BaseUI topBaseUI = CurrentUIPanelStack.Peek();
                //栈顶元素作冻结处理
                topBaseUI.Freeze();
            }

            //判断UI所有普窗体集合是否有指定的UI窗体，有则处理
            AllPanelDic.TryGetValue(panel, out baseUI);
            if (baseUI != null)
            {
                //当前窗体显示状态
                baseUI.Show();
                //把指定窗体作入栈操作
                CurrentUIPanelStack.Push(baseUI);
            }
            else
                Debug.Log("baseUI == null , Please check, 参数 panel " + panel);
        }

        /// <summary>
        /// 打开窗体，且隐藏其他窗体
        /// </summary>
        /// <param name="panel"></param>
        private void EnterUIPanelAndHideOthers(EnumPanel panel)
        {
            BaseUI baseUI = null;                   //UI窗体基类
            BaseUI baseUIFromAll = null;            //从集合中得到UI窗体基类

            if (panel == EnumPanel.None) return;
            //如果正在显示，则返回
            CurrentShowPanelDic.TryGetValue(panel, out baseUI);
            if (baseUI != null) return;

            //把正在显示集合与栈集合中所有窗体都隐藏
            foreach (BaseUI item in CurrentShowPanelDic.Values)
            {
                item.Hide();
            }
            foreach (BaseUI item in CurrentUIPanelStack)
            {
                item.Hide();
            }

            //把当前窗体加入到正在显示窗体集合中，并作显示处理
            AllPanelDic.TryGetValue(panel, out baseUIFromAll);
            if (baseUIFromAll != null)
            {
                CurrentShowPanelDic.Add(panel, baseUIFromAll);
                baseUIFromAll.Show();
            }
        }

        /// <summary>
        /// 退出指定UI窗体
        /// </summary>
        /// <param name="panel"></param>
        private void ExitUIPanel(EnumPanel panel)
        {
            BaseUI baseUI = null;

            //正在显示集合中如果没有记录，则直接返回
            CurrentShowPanelDic.TryGetValue(panel, out baseUI);
            if (baseUI == null) return;

            //指定窗体，标记为隐藏状态，且从正在显示集合中移除
            baseUI.Hide();
            CurrentShowPanelDic.Remove(panel);
        }

        /// <summary>
        /// （反向切换）窗体的出栈逻辑
        /// </summary>
        private void PopUIPanel()
        {
            if (CurrentUIPanelStack.Count >= 2)
            { 
                //出栈处理
                BaseUI topBaseUI = CurrentUIPanelStack.Pop();
                //作隐藏处理
                topBaseUI.Hide();
                //出栈后，下一个窗体作重新显示处理
                BaseUI nextBaseUI = CurrentUIPanelStack.Peek();
                nextBaseUI.ReShow();
            }
            else if (CurrentUIPanelStack.Count == 1)
            { 
                //出栈处理
                BaseUI topBaseUI = CurrentUIPanelStack.Pop();
                //作隐藏处理
                topBaseUI.Hide();
            }
        }

        /// <summary>
        /// 关闭窗体，且显示其他窗体
        /// </summary>
        /// <param name="panel"></param>
        private void ExitUIPanelAndShowOthers(EnumPanel panel)
        {
            BaseUI baseUI = null;

            if (panel == EnumPanel.None) return;

            CurrentShowPanelDic.TryGetValue(panel, out baseUI);
            if (baseUI == null) return;

            //当前窗体隐藏状态，且正在显示集合中，移除本窗体
            baseUI.Hide();
            CurrentShowPanelDic.Remove(panel);

            //把正在显示集合与栈集合中所有窗体都定义重新显示状态
            foreach (BaseUI item in CurrentShowPanelDic.Values)
            {
                item.ReShow();
            }
            foreach (BaseUI item in CurrentUIPanelStack)
            {
                item.ReShow();
            }
        }
        #endregion


        public void LoadSceneSync(string scene, Action beforeLoading = null, Action inLoading = null, Action afterLoading = null)
        {
            StartCoroutine(StartLoading(scene, beforeLoading, inLoading, afterLoading));
        }

        private IEnumerator OnLoadSceneSync(string scene, Action beforeLoading, Action inLoading, Action afterLoading)
        {
            if (beforeLoading != null)
                beforeLoading();
            BaseUI loadingUI = ShowUIPanel(EnumPanel.UILoadingPanel);
            AsyncOperation async = SceneManager.LoadSceneAsync(scene);
            while (!async.isDone)
            {
                if (inLoading != null)
                    inLoading();
                if (loadingUI != null)
                    loadingUI.ShowUI(async.progress);
                yield return async;
            }
            if(async.isDone)
            {
                if (afterLoading != null)
                    afterLoading();
                if (loadingUI != null)
                    loadingUI = null;
                CloseUIPanel(EnumPanel.UILoadingPanel);
            }

            //GC.Collect();
            //Resources.UnloadUnusedAssets();
        }

        private IEnumerator StartLoading(string scene, Action beforeLoading, Action inLoading, Action afterLoading)
        {
            BaseUI loadingUI = ShowUIPanel(EnumPanel.UILoadingPanel);
            int displayProgress = 0;
            int toProgress = 0;
            if (beforeLoading != null)
                beforeLoading();
            AsyncOperation op = SceneManager.LoadSceneAsync(scene);
            op.allowSceneActivation = false;
            while (op.progress < 0.9f)
            {
                if (inLoading != null)
                    inLoading();
                toProgress = (int)op.progress * 100;
                while (displayProgress < toProgress)
                {
                    ++displayProgress;
                    if (loadingUI != null)
                        loadingUI.ShowUI(displayProgress);
                    yield return new WaitForEndOfFrame();
                }
            }

            toProgress = 100;
            while (displayProgress < toProgress)
            {
                if (inLoading != null)
                    inLoading();
                ++displayProgress;
                if (loadingUI != null)
                    loadingUI.ShowUI(displayProgress);
                yield return new WaitForEndOfFrame();
            }
            op.allowSceneActivation = true;
            CloseUIPanel(EnumPanel.UILoadingPanel);
            if (afterLoading != null)
                afterLoading();
        }
    }
}
